package com.couchcrowler.server;

import static com.google.appengine.api.labs.taskqueue.TaskOptions.Builder.url;

import java.util.ArrayList;
import java.util.Collection;
import java.util.List;

import javax.servlet.http.HttpSession;

import com.couchcrowler.client.GreetingService;
import com.couchcrowler.server.dao.ProfileDao;
import com.couchcrowler.server.parser.ProfileParser;
import com.couchcrowler.shared.FieldVerifier;
import com.couchcrowler.shared.ForgetfulSet;
import com.couchcrowler.shared.Profile;
import com.google.appengine.api.labs.taskqueue.Queue;
import com.google.appengine.api.labs.taskqueue.QueueFactory;
import com.google.gwt.user.server.rpc.RemoteServiceServlet;
import com.google.inject.Inject;
import com.google.inject.Singleton;

/**
 * The server side implementation of the RPC service.
 */
@Singleton
@SuppressWarnings("serial")
public class GreetingServiceImpl extends RemoteServiceServlet implements GreetingService {

	private static final String PROFILE_PARSER_QUEUE = "profileParser";
	
	@Inject
	private ProfileDao profileDao;
	
	@Inject
	private ProfileParser profileParser;
	
	public String greetServer(String input) throws IllegalArgumentException {
		// Verify that the input is valid. 
		if (!FieldVerifier.isValidName(input)) {
			// If the input is not valid, throw an IllegalArgumentException back to
			// the client.
			throw new IllegalArgumentException(
					"Name must be at least 4 characters long");
		}

		String serverInfo = getServletContext().getServerInfo();
		String userAgent = getThreadLocalRequest().getHeader("User-Agent");

		// Escape data from the client to avoid cross-site script vulnerabilities.
		input = escapeHtml(input);
		userAgent = escapeHtml(userAgent);

		return "Hello, " + input + "!<br><br>I am running " + serverInfo
				+ ".<br><br>It looks like you are using:<br>" + userAgent;
	}

	/**
	 * Escape an html string. Escaping data received from the client helps to
	 * prevent cross-site script vulnerabilities.
	 * 
	 * @param html the html string to escape
	 * @return the escaped string
	 */
	private String escapeHtml(String html) {
		if (html == null) {
			return null;
		}
		return html.replaceAll("&", "&amp;").replaceAll("<", "&lt;")
				.replaceAll(">", "&gt;");
	}

	@Override
	public Profile getProfile(String id) {
		return getProfileParser().parseId(id);
	}
	
	@Override
	public List<Profile> getProfiles(List<String> ids) {		
		List<Profile> profileList = getProfileDao().getProfiles(ids);
		List<String> missingIds = new ArrayList<String>();
		for (int i = 0; i < ids.size(); i++) {
			if (profileList.get(i) == null) {
				missingIds.add(ids.get(i));				
			}
		}
		
		Queue queue = QueueFactory.getQueue(PROFILE_PARSER_QUEUE);
		Collection<String> loadingIds = getLoadingIds();
		for (String id : missingIds) {			
			if (!loadingIds.contains(id)) {
				loadingIds.add(id);
				queue.add(url("/task/parse").param("id", id));				
			}
		}
		
		return profileList;
	}
	
	private static final String LOADING_IDS = "loadingIds";
	
	private Collection<String> getLoadingIds() {
		HttpSession session = getThreadLocalRequest().getSession();
		@SuppressWarnings("unchecked")
		Collection<String> result = (Collection<String>) session.getAttribute(LOADING_IDS);
		if (result == null) {
			result = new ForgetfulSet<String>(20 * 1000 * 60);
			session.setAttribute(LOADING_IDS, result);
		}
		
		return result;
	}

	public ProfileParser getProfileParser() {
		return profileParser;
	}

	public void setProfileParser(ProfileParser profileParser) {
		this.profileParser = profileParser;
	}

	public ProfileDao getProfileDao() {
		return profileDao;
	}

	public void setProfileDao(ProfileDao profileDao) {
		this.profileDao = profileDao;
	}
	
	
}
